Code
from google.colab import drive
drive.mount("/content/drive")Mounted at /content/drive
As of 2024, over 122 million people are displaced globally — yet the burden is far from equally shared. This report highlights that low- and middle-income countries, particularly in Africa and Asia, host the vast majority of the world’s refugees, often with limited infrastructure and strained resources. Countries such as Pakistan, Chad, and Uganda face disproportionate pressure, with refugees making up over 10% of their population in some cases.
In contrast, many high-income nations, despite their economic strength, host far fewer refugees, both in absolute terms and relative to population. This raises critical concerns about global equity, shared responsibility, and the disproportionate impact of displacement on already vulnerable regions.
Refugee trends have not been static. Between 2011 and 2017, countries such as Uganda, Sudan, and Ethiopia experienced sharp surges in refugee populations, driven largely by ongoing regional conflicts. While some areas have seen stabilization, countries like Chad and Pakistan continue to receive large inflows of displaced individuals, placing further strain on their social and economic systems.
This report also explores whether wealthier countries are stepping up to the crisis. The data reveals a consistent pattern: displacement does not follow economic power. Many of the world’s largest refugee-hosting countries are among the least wealthy. While countries like Jordan and Iran continue to play a key role, many affluent nations contribute significantly less per capita, raising questions of fairness in the global refugee response.
Displacement is rising — but global responsibility remains uneven. As conflict, climate, and inequality continue to drive forced migration, the world must do more to share the weight.
from google.colab import drive
drive.mount("/content/drive")Mounted at /content/drive
!pip install plotnine pandas geopandas
import pandas as pd
from plotnine import *
import geopandas as gpdRequirement already satisfied: plotnine in /usr/local/lib/python3.11/dist-packages (0.14.5)
Requirement already satisfied: pandas in /usr/local/lib/python3.11/dist-packages (2.2.2)
Requirement already satisfied: geopandas in /usr/local/lib/python3.11/dist-packages (1.0.1)
Requirement already satisfied: matplotlib>=3.8.0 in /usr/local/lib/python3.11/dist-packages (from plotnine) (3.10.0)
Requirement already satisfied: mizani~=0.13.0 in /usr/local/lib/python3.11/dist-packages (from plotnine) (0.13.3)
Requirement already satisfied: numpy>=1.23.5 in /usr/local/lib/python3.11/dist-packages (from plotnine) (2.0.2)
Requirement already satisfied: scipy>=1.8.0 in /usr/local/lib/python3.11/dist-packages (from plotnine) (1.14.1)
Requirement already satisfied: statsmodels>=0.14.0 in /usr/local/lib/python3.11/dist-packages (from plotnine) (0.14.4)
Requirement already satisfied: python-dateutil>=2.8.2 in /usr/local/lib/python3.11/dist-packages (from pandas) (2.8.2)
Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.11/dist-packages (from pandas) (2025.2)
Requirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.11/dist-packages (from pandas) (2025.2)
Requirement already satisfied: pyogrio>=0.7.2 in /usr/local/lib/python3.11/dist-packages (from geopandas) (0.10.0)
Requirement already satisfied: packaging in /usr/local/lib/python3.11/dist-packages (from geopandas) (24.2)
Requirement already satisfied: pyproj>=3.3.0 in /usr/local/lib/python3.11/dist-packages (from geopandas) (3.7.1)
Requirement already satisfied: shapely>=2.0.0 in /usr/local/lib/python3.11/dist-packages (from geopandas) (2.1.0)
Requirement already satisfied: contourpy>=1.0.1 in /usr/local/lib/python3.11/dist-packages (from matplotlib>=3.8.0->plotnine) (1.3.2)
Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.11/dist-packages (from matplotlib>=3.8.0->plotnine) (0.12.1)
Requirement already satisfied: fonttools>=4.22.0 in /usr/local/lib/python3.11/dist-packages (from matplotlib>=3.8.0->plotnine) (4.57.0)
Requirement already satisfied: kiwisolver>=1.3.1 in /usr/local/lib/python3.11/dist-packages (from matplotlib>=3.8.0->plotnine) (1.4.8)
Requirement already satisfied: pillow>=8 in /usr/local/lib/python3.11/dist-packages (from matplotlib>=3.8.0->plotnine) (11.1.0)
Requirement already satisfied: pyparsing>=2.3.1 in /usr/local/lib/python3.11/dist-packages (from matplotlib>=3.8.0->plotnine) (3.2.3)
Requirement already satisfied: certifi in /usr/local/lib/python3.11/dist-packages (from pyogrio>=0.7.2->geopandas) (2025.1.31)
Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.11/dist-packages (from python-dateutil>=2.8.2->pandas) (1.17.0)
Requirement already satisfied: patsy>=0.5.6 in /usr/local/lib/python3.11/dist-packages (from statsmodels>=0.14.0->plotnine) (1.0.1)
import pandas as pd
# Path to your Excel file in Drive
file_path = '/content/drive/MyDrive/Colab Notebooks/UNICEF_Regional_Metadata_Merged_Final.xlsx'
# Read the first sheet into a DataFrame
df = pd.read_excel(file_path, engine='openpyxl')
# (Optional) Peek at the top to verify it loaded correctly
df.head()| country | alpha_2_code | alpha_3_code | numeric_code | indicator | time_period | obs_value | sex | unit_multiplier | unit_of_measure | ... | inflation_consumer_prices_annual | life_expectancy_at_birth_total_years | military_expenditure_of_gdp | fossil_fuel_energy_consumption_of_total | gdp_growth_annual | birth_rate_crude_per_1_000_people | hospital_beds_per_1_000_people | un_sub_region | world_bank_income_groups_combined | population_total | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | Afghanistan | AF | AFG | 4 | Refugees by host country, per 1 USD GNI per ca... | 2003 | 0 | Total | NaN | per 1 USD GNI per capita | ... | NaN | 57.344 | NaN | NaN | 8.832278 | 47.350 | 0.39 | Southern Asia | Low Income | 22733049.0 |
| 1 | Afghanistan | AF | AFG | 4 | Refugees by host country, per 1 USD GNI per ca... | 2004 | 0 | Total | NaN | per 1 USD GNI per capita | ... | NaN | 57.944 | 2.431254 | NaN | 1.414118 | 46.330 | 0.39 | Southern Asia | Low Income | 23560654.0 |
| 2 | Afghanistan | AF | AFG | 4 | Refugees by host country, per 1 USD GNI per ca... | 2005 | 0 | Total | NaN | per 1 USD GNI per capita | ... | 12.686269 | 58.361 | 1.992066 | NaN | 11.229715 | 45.263 | 0.42 | Southern Asia | Low Income | 24404567.0 |
| 3 | Afghanistan | AF | AFG | 4 | Refugees by host country, per 1 USD GNI per ca... | 2006 | 0 | Total | NaN | per 1 USD GNI per capita | ... | 6.784597 | 58.684 | 1.896234 | NaN | 5.357403 | 44.721 | 0.42 | Southern Asia | Low Income | 25424094.0 |
| 4 | Afghanistan | AF | AFG | 4 | Refugees by host country, per 1 USD GNI per ca... | 2007 | 0 | Total | NaN | per 1 USD GNI per capita | ... | 8.680571 | 59.111 | 2.566267 | NaN | 13.826320 | 43.858 | 0.42 | Southern Asia | Low Income | 25909852.0 |
5 rows × 24 columns
# Summarize total refugees by UN Region
bar_data = (
df.groupby('un_region')['obs_value']
.sum()
.reset_index()
.rename(columns={'obs_value': 'Total_Refugees'})
)# Summarize total refugees by UN Region
bar_data = (
df.groupby('un_region')['obs_value']
.sum()
.reset_index()
.rename(columns={'obs_value': 'Total_Refugees'})
)
# Plot the bar chart nicely
(
ggplot(bar_data, aes(x='un_region', y='Total_Refugees', fill='un_region')) +
geom_bar(stat='identity') +
scale_fill_brewer(type='seq', palette='Blues') +
labs(
title='Total Refugees by UN Region',
x='UN Region',
y='Number of Refugees'
) +
theme_minimal() +
theme(
axis_text_x=element_text(angle=45, hjust=1) # Rotates the x-axis labels
)
)Africa and Asia bear the heaviest refugee burdens globally, hosting significantly larger displaced populations compared to other regions. Africa alone accounts for the highest number of refugees, followed closely by Asia. In contrast, regions such as Europe, Latin America, and Northern America contribute relatively less to global refugee hosting, highlighting geographic inequalities in refugee support. This disparity underscores the need for international collaboration to better distribute the humanitarian burden.
# Line Chart - Top 5 Host Countries Over Time
from plotnine import *
# Top 5 countries by total refugee hosting
top_countries = df.groupby('country')['obs_value'].sum().nlargest(5).index
line_data = df[df['country'].isin(top_countries)]
# Plot
(
ggplot(line_data, aes(x='time_period', y='obs_value', color='country')) +
geom_line(size=1.2) +
labs(
title="Refugee Trends Over Time - Top 5 Host Countries",
x="Year",
y="Number of Refugees"
) +
scale_color_brewer(type='qual', palette='Set1') + # Stronger, readable colors
theme_minimal() +
theme(
axis_text_x=element_text(size=10),
axis_text_y=element_text(size=10),
plot_title=element_text(size=14, weight="bold", ha="center"),
figure_size=(10, 5)
)
)This time-series visualization highlights major fluctuations in refugee numbers among the top 5 host countries. Notably, Pakistan and Uganda have experienced significant spikes and declines over the years, reflecting ongoing regional instability. Meanwhile, Chad, Congo, and Ethiopia have seen relatively steadier but persistent inflows of displaced populations.
import plotly.express as px
# Prepare scatter data again
scatter_data = (
df.groupby('country')[['obs_value', 'gdp_per_capita_constant_2015_us']]
.mean()
.reset_index()
)
# Create improved interactive plot
fig = px.scatter(
scatter_data,
x='gdp_per_capita_constant_2015_us',
y='obs_value',
hover_name='country', # only on hover!
labels={
'gdp_per_capita_constant_2015_us': 'GDP Per Capita (2015 US$)',
'obs_value': 'Average Number of Refugees'
},
title='Wealth vs Refugees Hosted',
template='simple_white',
color_discrete_sequence=['#0072B2'] # Nice blue color
)
fig.update_traces(marker=dict(size=8, opacity=0.7))
fig.update_layout(
title_font=dict(size=20, family='Arial', color='black'),
xaxis_title_font=dict(size=16),
yaxis_title_font=dict(size=16),
hoverlabel=dict(bgcolor="white", font_size=12)
)
fig.show()This scatter plot explores the relationship between a country’s wealth (GDP per capita) and the average number of refugees it hosts.
The visualization reveals that wealthier nations tend to host fewer refugees relative to their income levels, while lower-income countries carry a disproportionately higher refugee burden. Each point represents a country, and one can hover to discover specific details. The pattern underscores the imbalance in global refugee support responsibilities.
!pip install plotlyRequirement already satisfied: plotly in /usr/local/lib/python3.11/dist-packages (5.24.1)
Requirement already satisfied: tenacity>=6.2.0 in /usr/local/lib/python3.11/dist-packages (from plotly) (9.1.2)
Requirement already satisfied: packaging in /usr/local/lib/python3.11/dist-packages (from plotly) (24.2)
# First, create a new column for refugees per population (%)
df['refugees_per_population_%'] = (df['obs_value'] / df['population_total']) * 100import plotly.express as px
# Create choropleth map
fig = px.choropleth(
df,
locations="country",
locationmode="country names",
color="refugees_per_population_%",
color_continuous_scale="Blues_r", # reversed so high values are darker
title="Refugees per Population (%) by Country",
labels={'refugees_per_population_%': 'Refugees per Population %'}
)
fig.update_layout(
title_font=dict(size=22, family='Arial', color='black'),
geo=dict(
showframe=False,
showcoastlines=True,
projection_type="natural earth",
fitbounds="locations" # Zoom into the active countries
),
coloraxis_colorbar=dict(title="Refugees per Population %")
)
fig.show()This choropleth map highlights refugee burdens as a share of national populations.
Countries like Chad, Uganda, and Sudan stand out with darker shades, signaling intense pressure relative to population size.
The visualization emphasizes the unequal humanitarian responsibilities across nations, prompting reflection on global solidarity efforts.